#include "iservice_registry_impl.h"
#include "iservice_module_info_impl.h"
#include "../../include/module_holder.h"
#include "../../include/ivariant_impl.h"
#include "../../include/ivariant_helper.h"
#include "../../include/ienum_impl.h"

#include <algorithm>
#include <string>
#include <iterator>
#include <fstream>
#include <map>

namespace Boss
{

  const char IServiceRegistryImpl::EqTag[] = " = ";
  const char IServiceRegistryImpl::TabTag[] = "\t";
  const char IServiceRegistryImpl::ModuleTag[] = "Madule";
  const char IServiceRegistryImpl::ModuleNameTag[] = "Name";
  const char IServiceRegistryImpl::ModulePathTag[] = "Path";
  const char IServiceRegistryImpl::CoClassesTag[] = "CoClasses";
  const char IServiceRegistryImpl::CoClassTag[] = "CoClass";
  const char IServiceRegistryImpl::EndCoClassesTag[] = "EndCoClasses";
  const char IServiceRegistryImpl::EndModuleTag[] = "EndModule";


  TResult BOSS_CALL_TYPE IServiceRegistryImpl::GetModuleIdByCLSID(TGuid classId, IVariant **moduleId) const
  {
    if (!classId || !*classId || !moduleId || *moduleId)
      return rcBadParam;
    ISyncObject Lock(GetSynObj());
    if (!Modules.get() || Modules->empty())
      return rcFalse;
    for (ModuleInfoPool::const_iterator i = Modules->begin() ; i != Modules->end() ; ++i)
    {
      if (std::find(i->CoClassesIds.begin(), i->CoClassesIds.end(), std::string(classId)) != i->CoClassesIds.end())
      {
        try
        {
          RefObjPtr<IVariant> Value = IBaseImpl<IVariantImpl>::Create(GetSynObj());
          IVariantHelper Helper(Value);
          Helper = i->Id.c_str();
          return Value.QueryInterface(moduleId);
        }
        catch (std::exception &)
        {
          return rcFail;
        }
      }
    }
    return rcFalse;
  }

  TResult BOSS_CALL_TYPE IServiceRegistryImpl::GetModuleCoClasses(TGuid moduleId, IEnum **coClassIds) const
  {
    if (!moduleId || !*moduleId || !coClassIds || *coClassIds)
      return rcBadParam;
    ISyncObject Lock(GetSynObj());
    if (!Modules.get() || Modules->empty())
      return rcFalse;
    for (ModuleInfoPool::const_iterator i = Modules->begin() ; i != Modules->end() ; ++i)
    {
      if (i->Id == moduleId)
      {
        try
        {
          RefObjPtr<IEnumImpl> CoClasses = IBaseImpl<IEnumImpl>::Create(GetSynObj());
          for (ModuleInfo::StringList::const_iterator j = i->CoClassesIds.begin() ; j != i->CoClassesIds.end() ; ++j)
          {
            RefObjPtr<IVariant> Value = IBaseImpl<IVariantImpl>::Create(GetSynObj());
            IVariantHelper Helper(Value);
            Helper = j->c_str();
            CoClasses->AddItem(Value);
          }
          return CoClasses.QueryInterface(coClassIds);
        }
        catch (std::exception &)
        {
          return rcFail;
        }
      }
    }
    return rcFalse;
  }

  TResult BOSS_CALL_TYPE IServiceRegistryImpl::GetModulePath(TGuid moduleId, IVariant **path) const
  {
    if (!moduleId || !*moduleId || !path || *path)
      return rcBadParam;
    ISyncObject Lock(GetSynObj());
    if (!Modules.get() || Modules->empty())
      return rcFalse;
    for (ModuleInfoPool::const_iterator i = Modules->begin() ; i != Modules->end() ; ++i)
    {
      if (i->Id == moduleId)
      {
        try
        {
          RefObjPtr<IVariant> Value = IBaseImpl<IVariantImpl>::Create(GetSynObj());
          IVariantHelper Helper(Value);
          Helper = i->Path.c_str();
          return Value.QueryInterface(path);
        }
        catch (std::exception &)
        {
          return rcFail;
        }
      }
    }
    return rcFalse;
  }

  TResult BOSS_CALL_TYPE IServiceRegistryImpl::EnumAllModules(IEnum **modules) const
  {
    if (!modules || *modules)
      return rcBadParam;
    ISyncObject Lock(GetSynObj());
    if (!Modules.get() || Modules->empty())
      return rcFalse;
    try
    {
      RefObjPtr<IEnumImpl> ModulesInfo = IBaseImpl<IEnumImpl>::Create(GetSynObj());
      for (ModuleInfoPool::const_iterator i = Modules->begin() ; i != Modules->end() ; ++i)
      {
        RefObjPtr<IServiceModuleInfoImpl> Info = IBaseImpl<IServiceModuleInfoImpl>::Create(GetSynObj());
        Info->SetModuleId(i->Id);
        Info->SetModulePath(i->Path);
        Info->SetModuleName(i->Name);
        for (ModuleInfo::StringList::const_iterator j = i->CoClassesIds.begin() ; j!= i->CoClassesIds.end() ; ++j)
          Info->AddModuleCoClassesId(*j);
        ModulesInfo->AddItem(Info);
      }
      return ModulesInfo.QueryInterface(modules);
    }
    catch (std::exception &)
    {
      return rcFail;
    }
    return rcFalse;
  }

  TResult BOSS_CALL_TYPE IServiceRegistryImpl::Init(IVariant *path)
  {
    if (!path)
      return rcBadParam;
    try
    {
      ISyncObject Lock(GetSynObj());
      if (!FileName.empty())
        return rcFail;
      FileName = (const std::string::value_type *)IVariantHelper(path);
      ModuleInfoPoolPtr NewModulesInfo = LoadRegistry();
      if (!NewModulesInfo.get())
        return rcFalse;
      Modules = NewModulesInfo;
    }
    catch (std::exception &)
    {
      return rcFail;
    }
    return rcOk;
  }

  TResult BOSS_CALL_TYPE IServiceRegistryImpl::RegModule(IVariant *path)
  {
    if (!path)
      return rcBadParam;
    try
    {
      ISyncObject Lock(GetSynObj());
      ModuleInfo Info;
      Info.Path = (const std::string::value_type *)IVariantHelper(path);
      ModuleHolder Module(SharedPtr<DllHolder>(new DllHolder(Info.Path.c_str())));
      Info.Name = Module.GetModuleName();
      Info.Id = Module.GetModuleGuid();
      ModuleHolder::GuidPool CoClasses = Module.GetCoClassIds();
      std::copy(CoClasses.begin(), CoClasses.end(), std::back_inserter(Info.CoClassesIds));
      if (!Modules.get() || Modules->empty())
      {
        ModuleInfoPoolPtr NewModules(new ModuleInfoPool);
        NewModules->push_back(Info);
        SaveRegistry(*NewModules.get());
        Modules = NewModules;
      }
      for (ModuleInfoPool::iterator i = Modules->begin() ; i != Modules->end() ; ++i)
      {
        if (i->Id == Info.Id)
        {
          bool IsEqual = Info.Name == i->Name;
          if (IsEqual)
            IsEqual = Info.Path == i->Path;
          if (IsEqual)
            IsEqual = Info.CoClassesIds == i->CoClassesIds;
          if (!IsEqual)
          {
            ModuleInfo CurInfo = *i;
            *i = Info;
            try
            {
              SaveRegistry(*Modules.get());
            }
            catch (std::exception &)
            {
              *i = CurInfo;
              throw;
            }
          }
          return rcOk;
        }
      }
      Modules->push_back(Info);
      try
      {
        SaveRegistry(*Modules.get());
      }
      catch (std::exception &)
      {
        for (ModuleInfoPool::iterator i = Modules->begin() ; i != Modules->end() ; ++i)
        {
          if (i->Id == Info.Id)
          {
            Modules->erase(i);
            break;
          }
        }
        throw;
      }
    }
    catch (std::exception &)
    {
      return rcFail;
    }
    return rcOk;
  }

  TResult BOSS_CALL_TYPE IServiceRegistryImpl::UnRegModule(IVariant *path)
  {
    if (!path)
      return rcBadParam;
    try
    {
      ISyncObject Lock(GetSynObj());
      if (!Modules.get() || Modules->empty())
        return rcFail;
      ModuleHolder Module(SharedPtr<DllHolder>(new DllHolder((const std::string::value_type *)IVariantHelper(path))));
      std::string Id = Module.GetModuleGuid();
      for (ModuleInfoPool::iterator i = Modules->begin() ; i != Modules->end() ; ++i)
      {
        if (i->Id == Id)
        {
          ModuleInfo CurInfo = *i;
          Modules->erase(i);
          try
          {
            SaveRegistry(*Modules.get());
          }
          catch (...)
          {
            Modules->push_back(CurInfo);
            throw;
          }
          break;
        }
      }
    }
    catch (std::exception &)
    {
      return rcFail;
    }
    return rcOk;
  }

  IServiceRegistryImpl::ModuleInfoPoolPtr IServiceRegistryImpl::LoadRegistry() const
  {
    BOSS_DECLARE_RUNTIME_EXCEPTION(RegistryFormat)

    class
    {
    public:
      ModuleInfoPoolPtr operator () (std::istream &stream) const
      {
        ModuleInfoPoolPtr RetPool;
        for (ModuleInfoPtr Module ; (Module = GetModule(stream)).get() ; )
        {
          if (!RetPool.get())
            RetPool.reset(new ModuleInfoPool);
          RetPool->push_back(*Module.get());
        }
        return RetPool;
      }

    private:
      typedef std::auto_ptr<ModuleInfo> ModuleInfoPtr;
      typedef std::pair<std::string, std::string> StringPair;
      StringPair ParseLine(const std::string &s) const
      {
        std::string::size_type Pos = s.find(EqTag);
        if (Pos == std::string::npos)
          throw RegistryFormatException("Bad format");
        StringPair Ret(s.substr(0, Pos), s.substr(Pos + std::string(EqTag).length()));
        if ((Pos = Ret.first.find_first_not_of(' ')) != std::string::npos)
          Ret.first = Ret.first.substr(Pos);
        if ((Pos = Ret.first.find_first_not_of('\t')) != std::string::npos)
          Ret.first = Ret.first.substr(Pos);
        if ((Pos = Ret.second.find_first_not_of(' ')) != std::string::npos)
          Ret.second = Ret.second.substr(Pos);
        if ((Pos = Ret.second.find_first_not_of('\t')) != std::string::npos)
          Ret.second = Ret.second.substr(Pos);
        if ((Pos = Ret.first.find_last_not_of(' ')) != std::string::npos)
          Ret.first = Ret.first.substr(0, Pos + 1);
        if ((Pos = Ret.first.find_last_not_of('\t')) != std::string::npos)
          Ret.first = Ret.first.substr(0, Pos + 1);
        if ((Pos = Ret.second.find_last_not_of(' ')) != std::string::npos)
          Ret.second = Ret.second.substr(0, Pos + 1);
        if ((Pos = Ret.second.find_last_not_of('\t')) != std::string::npos)
          Ret.second = Ret.second.substr(0, Pos + 1);
        return Ret;
      }
      ModuleInfoPtr GetModule(std::istream &stream) const
      {
        ModuleInfoPtr RetInfo;
        bool Begin = false;
        for (std::string s ; std::getline(stream, s) ; )
        {
          if (s.empty())
            continue;
          if (!Begin)
          {
            if (s.find(ModuleTag) == std::string::npos)
              throw RegistryFormatException("Module not found");
            Begin = true;
            StringPair Items = ParseLine(s);
            if (Items.first != ModuleTag || Items.second.empty())
              throw RegistryFormatException("Bad format");
            RetInfo.reset(new ModuleInfo);
            RetInfo->Id = Items.second;
            continue;
          }
          if (s.find(ModuleNameTag) != std::string::npos)
          {
            StringPair Items = ParseLine(s);
            if (Items.first != ModuleNameTag || Items.second.empty())
              throw RegistryFormatException("Bad format");
            RetInfo->Name = Items.second;
            continue;
          }
          if (s.find(ModulePathTag) != std::string::npos)
          {
            StringPair Items = ParseLine(s);
            if (Items.first != ModulePathTag || Items.second.empty())
              throw RegistryFormatException("Bad format");
            RetInfo->Path = Items.second;
            continue;
          }
          if (s.find(CoClassesTag) != std::string::npos)
          {
            while (std::getline(stream, s) && s.find(EndCoClassesTag) == std::string::npos)
            {
              if (s.empty())
                continue;
              if (s.find(CoClassTag) != std::string::npos)
              {
                StringPair Items = ParseLine(s);
                if (Items.first != CoClassTag || Items.second.empty())
                  throw RegistryFormatException("Bad format");
                RetInfo->CoClassesIds.push_back(Items.second);
              }
            }
            continue;
          }
          if (s.find(EndModuleTag) != std::string::npos)
            break;
        }
        return RetInfo;
      }
    } Parse;
    std::fstream File(FileName.c_str(), std::ios_base::in);
    return Parse(File);
  }

  void IServiceRegistryImpl::SaveRegistry(const ModuleInfoPool &modules) const
  {
    std::ofstream File(FileName.c_str(), std::ios_base::out | std::ios_base::trunc);
    for (ModuleInfoPool::const_iterator i = modules.begin() ; i != modules.end() ; ++i)
    {
      File
        << ModuleTag << EqTag << i->Id << std::endl
        << TabTag << ModuleNameTag << EqTag << i->Name << std::endl
        << TabTag << ModulePathTag << EqTag << i->Path << std::endl
        << TabTag << CoClassesTag << std::endl;

      for (ModuleInfo::StringList::const_iterator j = i->CoClassesIds.begin() ; j != i->CoClassesIds.end() ; ++j)
        File << TabTag << TabTag << CoClassTag << EqTag << *j << std::endl;

      File
        << TabTag << EndCoClassesTag << std::endl
        << EndModuleTag << std::endl << std::endl;
    }
    File << std::endl;
  }

}
